//! mstatus寄存器0x300
//!

use crate::{bits, register::misa::XLEN, result};
use core::arch::asm;

#[repr(C)]
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct Mstatus {
    bits: usize,
}
impl Mstatus {
    ///  CSR类型的合法位域的位掩码
    pub const BITMASK: usize = 0x8000_0000_007f_fffe;

    ///   从原始比特值创建新的CSR类型。
    ///
    ///   仅仅掩码中的位 [BITMASK] ( Self :: BITMASK )  会被设置。
    pub const fn from_bits(bits: usize) -> Self {
        Self {
            bits: bits & 0x8000_0000_007f_fffe,
        }
    }

    ///   获取有效bit的值.
    pub const fn bits(&self) -> usize {
        self.bits & 0x8000_0000_007f_fffe
    }

    /// 获取mstatus有效位域的掩码
    pub const fn bitmask(&self) -> usize {
        Self::BITMASK
    }
}
///   读取mstatus寄存器的usize类型数据。
#[inline(always)]
unsafe fn _read() -> usize {
    let r: usize;
    unsafe {
        asm!(
        "csrrs {0}, 0x300, zero",
        out(reg)r)
    }
    r
}

///  读取mstatus寄存器的usize类型数据,以Mstatus类型返回
#[inline]
pub fn read() -> Mstatus {
    Mstatus {
        bits: unsafe { _read() },
    }
}

/// 写入usize类型的数据到mstatus
#[inline(always)]
unsafe fn _write(bits: usize) {
    unsafe {
        asm!(
            "csrrw zero, 0x300, {0}",
            in(reg) bits
        );
    }
}

///  写入Mstatus结构体类型到mstatus中。
#[inline]
pub fn write(value: Mstatus) {
    unsafe { _write(value.bits) }
}

/// bits这个值和mstatus寄存器的值进行了或操作。
/// 设置mstatus的某些位
#[inline(always)]
unsafe fn _set(bits: usize) {
    unsafe {
        asm!(
            "csrrs zero, 0x300, {0}",
            in(reg) bits
        );
    }
}

#[inline(always)]
unsafe fn _clear(bits: usize) {
    unsafe {
        asm!(
            "csrrc zero, 0x300, {0}",
            in(reg) bits    // bits为1的位是要清楚的位
        )
    }
}

#[inline]
pub fn set_uie() {
    unsafe {
        _set(1 << 0);
    }
}

#[inline]
pub fn clear_uie() {
    unsafe {
        _clear(1 << 0);
    }
}

#[inline]
pub fn set_sie() {
    unsafe {
        _set(1 << 1);
    }
}

#[inline]
pub fn clear_sie() {
    unsafe {
        _clear(1 << 1);
    }
}

#[inline]
pub fn set_mie() {
    unsafe {
        _set(1 << 3);
    }
}

#[inline]
pub fn clear_mie() {
    unsafe {
        _clear(1 << 3);
    }
}

#[inline]
pub fn set_upie() {
    unsafe {
        _set(1 << 4);
    }
}

#[inline]
pub fn set_spie() {
    unsafe {
        _set(1 << 5);
    }
}

#[inline]
pub fn set_mpie() {
    unsafe {
        _set(1 << 7);
    }
}

#[inline]
pub fn set_mprv() {
    unsafe {
        _set(1 << 17);
    }
}

#[inline]
pub fn clear_mprv() {
    unsafe {
        _clear(1 << 17);
    }
}

#[inline]
pub unsafe fn set_sum() {
    unsafe {
        _set(1 << 18);
    }
}

#[inline]
pub fn clear_sum() {
    unsafe {
        _clear(1 << 18);
    }
}

#[inline]
pub fn set_mxr() {
    unsafe {
        _set(1 << 19);
    }
}

#[inline]
pub fn clear_mxr() {
    unsafe {
        _clear(1 << 19);
    }
}

#[inline]
pub fn set_tvm() {
    unsafe {
        _set(1 << 20);
    }
}

#[inline]
pub fn clear_tvm() {
    unsafe {
        _clear(1 << 20);
    }
}

#[inline]
pub fn set_tw() {
    unsafe {
        _set(1 << 21);
    }
}

#[inline]
pub fn clear_tw() {
    unsafe {
        _clear(1 << 21);
    }
}

#[inline]
pub fn set_tsr() {
    unsafe {
        _set(1 << 22);
    }
}

#[inline]
pub fn clear_tsr() {
    unsafe {
        _clear(1 << 22);
    }
}

#[inline]
pub fn set_ube(endianness: Endianness) {
    match endianness {
        Endianness::BigEndian => unsafe { _set(1 << 6) },
        Endianness::LittleEndian => unsafe { _clear(1 << 6) },
    }
}

#[inline]
pub fn set_spp(spp: SPP) {
    match spp {
        SPP::Supervisor => unsafe { _set(1 << 8) },
        SPP::User => unsafe { _clear(1 << 8) },
    }
}

#[inline]
pub fn set_mpp(mpp: MPP) {
    let mut value = unsafe { _read() };
    value &= !(0x3 << 11); // clear previous value
    value |= (mpp as usize) << 11;
    unsafe {
        _write(value);
    }
}

#[inline]
pub fn set_fs(fs: FS) {
    let mut value = unsafe { _read() };
    value &= !(0x3 << 13); // clear previous value
    value |= (fs as usize) << 13;
    unsafe {
        _write(value);
    }
}

#[inline]
pub fn set_vs(vs: VS) {
    let mut value = unsafe { _read() };
    value &= !(0x3 << 9); // clear previous value
    value |= (vs as usize) << 9;
    unsafe {
        _write(value);
    }
}

#[inline]
pub fn set_sbe(endianness: Endianness) {
    match endianness {
        Endianness::BigEndian => unsafe { _set(1 << 36) },
        Endianness::LittleEndian => unsafe { _clear(1 << 36) },
    }
}

#[inline]
pub fn set_mbe(endianness: Endianness) {
    match endianness {
        Endianness::BigEndian => unsafe { _set(1 << 37) },
        Endianness::LittleEndian => unsafe { _clear(1 << 37) },
    }
}

// ----------------------------------------------------------------------
/// mstatus的XS域[16:15], 用来使能U模式下扩展的其他状态
#[repr(usize)]
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum XS {
    /// 全部关闭
    AllOff = 0,
    /// 没有脏的或者有些是干净的
    NoneDirtyOrClean = 1,
    /// 不脏，有干净的
    NoneDirtySomeClean = 2,
    /// 有些脏的
    SomeDirty = 3,
}
impl XS {
    pub const fn new() -> Self {
        Self::AllOff
    }

    pub const fn from_usize(val: usize) -> result::Result<Self> {
        match val {
            0 => Ok(Self::AllOff),
            1 => Ok(Self::NoneDirtyOrClean),
            2 => Ok(Self::NoneDirtySomeClean),
            3 => Ok(Self::SomeDirty),
            _ => Err(result::Error::InvalidVariant(val)),
        }
    }

    pub const fn into_usize(self) -> usize {
        self as usize
    }
}
impl Default for XS {
    fn default() -> Self {
        Self::new()
    }
}
impl From<XS> for usize {
    fn from(val: XS) -> Self {
        val.into_usize()
    }
}
impl TryFrom<usize> for XS {
    type Error = result::Error;

    fn try_from(val: usize) -> result::Result<Self> {
        Self::from_usize(val)
    }
}

// ----------------------------------------------------------------------
/// FS:[14:13]:用来使能浮点单元
#[repr(usize)]
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum FS {
    Off = 0,
    Initial = 1,
    Clean = 2,
    Dirty = 3,
}
impl FS {
    pub const fn new() -> Self {
        Self::Off
    }

    pub const fn from_usize(val: usize) -> result::Result<Self> {
        match val {
            0 => Ok(Self::Off),
            1 => Ok(Self::Initial),
            2 => Ok(Self::Clean),
            3 => Ok(Self::Dirty),
            _ => Err(result::Error::InvalidVariant(val)),
        }
    }

    pub const fn into_usize(self) -> usize {
        self as usize
    }
}
impl Default for FS {
    fn default() -> Self {
        Self::new()
    }
}
impl From<FS> for usize {
    fn from(val: FS) -> Self {
        val.into_usize()
    }
}
impl TryFrom<usize> for FS {
    type Error = result::Error;

    fn try_from(val: usize) -> result::Result<Self> {
        Self::from_usize(val)
    }
}
// ----------------------------------------------------------------------
/// VS:[10:9]:用来使能可伸缩矢量扩展
#[repr(usize)]
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum VS {
    Off = 0,
    Initial = 1,
    Clean = 2,
    Dirty = 3,
}
impl VS {
    pub const fn new() -> Self {
        Self::Off
    }

    pub const fn from_usize(val: usize) -> result::Result<Self> {
        match val {
            0 => Ok(Self::Off),
            1 => Ok(Self::Initial),
            2 => Ok(Self::Clean),
            3 => Ok(Self::Dirty),
            _ => Err(result::Error::InvalidVariant(val)),
        }
    }

    pub const fn into_usize(self) -> usize {
        self as usize
    }
}
impl Default for VS {
    fn default() -> Self {
        Self::new()
    }
}
impl From<VS> for usize {
    fn from(val: VS) -> Self {
        val.into_usize()
    }
}
impl TryFrom<usize> for VS {
    type Error = result::Error;

    fn try_from(val: usize) -> result::Result<Self> {
        Self::from_usize(val)
    }
}
// ----------------------------------------------------------------------
/// MPP:[12:11]:陷入M模式之前CPU的处理模式 0:U到M  1:S陷入M   2:在M模式触发的异常
#[repr(usize)]
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
#[allow(clippy::upper_case_acronyms)]
pub enum MPP {
    User = 0,
    Supervisor = 1,
    Machine = 3,
}
impl MPP {
    pub const fn new() -> Self {
        Self::User
    }

    pub const fn from_usize(val: usize) -> result::Result<Self> {
        match val {
            0 => Ok(Self::User),
            1 => Ok(Self::Supervisor),
            3 => Ok(Self::Machine),
            _ => Err(result::Error::InvalidVariant(val)),
        }
    }

    pub const fn into_usize(self) -> usize {
        self as usize
    }
}
impl Default for MPP {
    fn default() -> Self {
        Self::new()
    }
}
impl From<MPP> for usize {
    fn from(val: MPP) -> Self {
        val.into_usize()
    }
}
impl TryFrom<usize> for MPP {
    type Error = result::Error;

    fn try_from(val: usize) -> result::Result<Self> {
        Self::from_usize(val)
    }
}
// ----------------------------------------------------------------------
/// SPP:[8]:陷入S模式之前的CPU的处理模式 0:从U模式陷入S模式    1:在S模式触发的异常
#[repr(usize)]
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
#[allow(clippy::upper_case_acronyms)]
pub enum SPP {
    User = 0,
    Supervisor = 1,
}
impl SPP {
    pub const fn new() -> Self {
        Self::User
    }

    pub const fn from_usize(val: usize) -> result::Result<Self> {
        match val {
            0 => Ok(Self::User),
            1 => Ok(Self::Supervisor),
            _ => Err(result::Error::InvalidVariant(val)),
        }
    }

    pub const fn into_usize(self) -> usize {
        self as usize
    }
}
impl Default for SPP {
    fn default() -> Self {
        Self::new()
    }
}
impl From<SPP> for usize {
    fn from(val: SPP) -> Self {
        val.into_usize()
    }
}
impl TryFrom<usize> for SPP {
    type Error = result::Error;

    fn try_from(val: usize) -> result::Result<Self> {
        Self::from_usize(val)
    }
}
// ----------------------------------------------------------------------
/// UBE:[6]:用来控制U模式下加载和存储指令访问内存的大小端模式。0:小端模式    1:大端模式
#[repr(usize)]
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
#[allow(clippy::upper_case_acronyms)]
pub enum Endianness {
    LittleEndian = 0,
    BigEndian = 1,
}

impl Endianness {
    pub const fn new() -> Self {
        Self::LittleEndian
    }

    pub const fn from_usize(val: usize) -> result::Result<Self> {
        match val {
            0 => Ok(Self::LittleEndian),
            1 => Ok(Self::BigEndian),
            _ => Err(result::Error::InvalidVariant(val)),
        }
    }

    pub const fn into_usize(self) -> usize {
        self as usize
    }
}

impl Default for Endianness {
    fn default() -> Self {
        Self::new()
    }
}
impl From<Endianness> for usize {
    fn from(val: Endianness) -> Self {
        val.into_usize()
    }
}
impl TryFrom<usize> for Endianness {
    type Error = result::Error;

    fn try_from(val: usize) -> result::Result<Self> {
        Self::from_usize(val)
    }
}

impl From<bool> for Endianness {
    fn from(val: bool) -> Self {
        match val {
            false => Self::LittleEndian,
            true => Self::BigEndian,
        }
    }
}
// ----------------------------------------------------------------------

impl Mstatus {
    /// SIE:[1]:中断使能位，用来使能和关闭S模式下的所有中断。是否打开中断
    #[inline]
    pub fn sie(&self) -> bool {
        bits::bf_extract(self.bits, 1, 1) != 0
    }
    /// SIE中断打开和关闭操作
    #[inline]
    pub fn set_sie(&mut self, set_sie: bool) {
        self.bits = bits::bf_insert(self.bits, 1, 1, set_sie as usize);
    }

    /// MIE:[3]:用来使能和关闭M模式下的所有中断。mie是否打开中断
    #[inline]
    pub fn mie(&self) -> bool {
        bits::bf_extract(self.bits, 3, 1) != 0
    }

    /// MIE设置中断
    #[inline]
    pub fn set_mie(&mut self, set_mie: bool) {
        self.bits = bits::bf_insert(self.bits, 3, 1, set_mie as usize);
    }

    /// SPIE[5]:中断使能位。当一个异常陷入S模式时，SIE的值保存到SPIE中，SIE设置为0.
    /// 当调用SRET指令时，从SPIE中恢复SIE，然后把SPIE设置为1.
    /// 查看SPIE是否被设置。
    #[inline]
    pub fn spie(&self) -> bool {
        bits::bf_extract(self.bits, 5, 1) != 0
    }

    /// SPIE设置操作
    #[inline]
    pub fn set_spie(&mut self, set_spie: bool) {
        self.bits = bits::bf_insert(self.bits, 5, 1, set_spie as usize);
    }

    /// UBE:[6]:用来控制U模式下加载和存储指令访问内存的大小端模式。0:小端模式    1:大端模式
    /// 是否使用了大端模式
    #[inline]
    pub fn ube(&self) -> bool {
        bits::bf_extract(self.bits, 6, 1) != 0
    }

    /// 设置UBE,false是小端模式，true为大端模式
    #[inline]
    pub fn set_ube(&mut self, set_ube: bool) {
        self.bits = bits::bf_insert(self.bits, 6, 1, set_ube as usize);
    }

    /// MPIE[7]:中断使能保存位，当一个异常陷入M模式时，MIE的值保存到MPIE中，MIE设置为0.
    /// 当调用MRET指令返回时，从MPIE中恢复MIE，然后MPIE设置为1.
    /// 判断MIPE是否设置。
    #[inline]
    pub fn mpie(&self) -> bool {
        bits::bf_extract(self.bits, 7, 1) != 0
    }

    /// 设置MPIE。
    #[inline]
    pub fn set_mpie(&mut self, set_mpie: bool) {
        self.bits = bits::bf_insert(self.bits, 7, 1, set_mpie as usize);
    }

    /// SPP[8]: 陷入S模式之前CPU的处理模式。
    #[inline]
    pub fn spp(&self) -> SPP {
        self.try_spp().unwrap()
    }

    #[inline]
    pub fn try_spp(&self) -> result::Result<SPP> {
        let value = bits::bf_extract(self.bits, 8, 1);

        SPP::from_usize(value)
    }

    #[inline]
    pub fn set_spp(&mut self, set_spp: SPP) {
        self.bits = bits::bf_insert(self.bits, 8, 1, set_spp.into());
    }

    /// VS[10:9]:使能可伸缩矢量扩展
    #[inline]
    pub fn vs(&self) -> VS {
        self.try_vs().unwrap()
    }

    #[inline]
    pub fn try_vs(&self) -> result::Result<VS> {
        let value = bits::bf_extract(self.bits, 9, 2);

        VS::from_usize(value)
    }

    #[inline]
    pub fn set_vs(&mut self, set_vs: VS) {
        self.bits = bits::bf_insert(self.bits, 9, 2, set_vs.into());
    }

    /// MPP[12:11]:陷入M模式之前CPU的处理模式
    #[inline]
    pub fn mpp(&self) -> MPP {
        self.try_mpp().unwrap()
    }

    #[inline]
    pub fn try_mpp(&self) -> result::Result<MPP> {
        let value = bits::bf_extract(self.bits, 11, 2);

        MPP::from_usize(value)
    }

    #[inline]
    pub fn set_mpp(&mut self, set_mpp: MPP) {
        self.bits = bits::bf_insert(self.bits, 11, 2, set_mpp.into());
    }

    /// FS[14:13]:用来使能浮点单元
    #[inline]
    pub fn fs(&self) -> FS {
        self.try_fs().unwrap()
    }

    #[inline]
    pub fn try_fs(&self) -> result::Result<FS> {
        let value = bits::bf_extract(self.bits, 13, 1);

        FS::from_usize(value)
    }

    #[inline]
    pub fn set_fs(&mut self, set_fs: FS) {
        self.bits = bits::bf_insert(self.bits, 13, 2, set_fs.into());
    }

    /// XS[16:15]:用来使能U模式下扩展的其他状态
    #[inline]
    pub fn xs(&self) -> XS {
        self.try_xs().unwrap()
    }

    #[inline]
    pub fn try_xs(&self) -> result::Result<XS> {
        let value = bits::bf_extract(self.bits, 15, 2);

        XS::from_usize(value)
    }

    #[inline]
    pub fn set_xs(&mut self, set_xs: XS) {
        self.bits = bits::bf_insert(self.bits, 15, 2, set_xs.into());
    }

    /// MPRV[17]:用来修改有关特权模式。0：加载和存储指令按照当前的处理器模式进行地址转换与内存保护
    /// 1：加载和存储指令按照MPP字段中存储的处理器模式的权限进行内存保护与检查。
    #[inline]
    pub fn mprv(&self) -> bool {
        bits::bf_extract(self.bits, 17, 1) != 0
    }

    #[inline]
    pub fn set_mprv(&mut self, set_mprv: bool) {
        self.bits = bits::bf_insert(self.bits, 17, 1, set_mprv as usize);
    }

    /// SUM[18]: 指定在S模式下是否允许访问U模式的内存。0：在S模式下访问U模式下的内存会触发异常。
    /// 1：在S模式下可以访问U模式下的内存。
    #[inline]
    pub fn sum(&self) -> bool {
        bits::bf_extract(self.bits, 18, 1) != 0
    }

    #[inline]
    pub fn set_sum(&mut self, set_sum: bool) {
        self.bits = bits::bf_insert(self.bits, 18, 1, set_sum as usize);
    }

    /// MXR[19]:指定内存访问权限。0：可以加载只读页面 1：可以加载可读和可执行的页面。
    #[inline]
    pub fn mxr(&self) -> bool {
        bits::bf_extract(self.bits, 19, 1) != 0
    }

    #[inline]
    pub fn set_mxr(&mut self, set_mxr: bool) {
        self.bits = bits::bf_insert(self.bits, 19, 1, set_mxr as usize);
    }

    /// TVM[20]: 支持拦截S模式下的虚拟内存管理操作。
    /// 0: 在S模式下可以正常访问satp系统寄存器或执行SFENCE.VMA/SINVAL.VMA指令
    /// 1：在S模式下访问satp系统寄存器或者执行SFENCE.VMA/SINVAL.VMA指令会触发一个非法指令异常
    #[inline]
    pub fn tvm(&self) -> bool {
        bits::bf_extract(self.bits, 20, 1) != 0
    }

    #[inline]
    pub fn set_tvm(&mut self, set_tvm: bool) {
        self.bits = bits::bf_insert(self.bits, 20, 1, set_tvm as usize);
    }

    /// TW[21]: 支持拦截WFI指令。
    /// 0：WFI指令可以在低特权模式下执行。
    /// 1：如果WFI指令以任何低特权模式执行，并且它没有在特定视线中约定的有限时间内完成，就会触发一个非法指令异常。
    #[inline]
    pub fn tw(&self) -> bool {
        bits::bf_extract(self.bits, 21, 1) != 0
    }

    #[inline]
    pub fn set_tw(&mut self, set_tw: bool) {
        self.bits = bits::bf_insert(self.bits, 21, 1, set_tw as usize);
    }

    /// TSR[22]:支持拦截SRET指令。
    /// 0：在S模式下正常执行SRET指令。
    /// 1：在S模式下执行SRET指令会触发一个非法指令异常。
    #[inline]
    pub fn tsr(&self) -> bool {
        bits::bf_extract(self.bits, 22, 1) != 0
    }

    #[inline]
    pub fn set_tsr(&mut self, set_tsr: bool) {
        self.bits = bits::bf_insert(self.bits, 22, 1, set_tsr as usize);
    }

    /// UXL[33:32]:用来表示U模式下寄存器的长度。
    #[inline]
    pub fn uxl(&self) -> XLEN {
        XLEN::try_from(bits::bf_extract(self.bits, 32, 2)).unwrap_or_default()
    }

    #[inline]
    pub fn set_uxl(&mut self, uxl: XLEN) {
        self.bits = bits::bf_insert(self.bits, 32, 2, uxl as usize);
    }

    /// SXL[35:34]:用来表示S模式下寄存器的长度
    #[inline]
    pub fn sxl(&self) -> XLEN {
        XLEN::try_from(bits::bf_extract(self.bits, 34, 2)).unwrap_or_default()
    }

    #[inline]
    pub fn set_sxl(&mut self, sxl: XLEN) {
        self.bits = bits::bf_insert(self.bits, 34, 2, sxl as usize);
    }

    /// SBE[36]:用来控制S模式下加载和内存访问的大小端模式
    /// 0: 小端模式  1：大端模式
    pub fn sbe(&self) -> Endianness {
        Endianness::from(bits::bf_extract(self.bits, 36, 1) != 0)
    }

    #[inline]
    #[cfg(not(target_arch = "riscv32"))]
    pub fn set_sbe(&mut self, endianness: Endianness) {
        self.bits = bits::bf_insert(self.bits, 36, 1, endianness as usize);
    }

    /// MBE[37]: 用来控制M模式下的内存大小端模式,值同上.
    pub fn mbe(&self) -> Endianness {
        Endianness::try_from(bits::bf_extract(self.bits, 37, 1)).unwrap_or_default()
    }

    pub fn set_mbe(&mut self, endianness: Endianness) {
        self.bits = bits::bf_insert(self.bits, 37, 1, endianness as usize);
    }

    /// SD[63]:用来表示VS、FS以及XS中任意一个字段已经设置。
    #[inline]
    pub fn sd(&self) -> bool {
        bits::bf_extract(self.bits, 63, 1) != 0
    }

    #[inline]
    pub fn set_sd(&mut self, set_sd: bool) {
        self.bits = bits::bf_insert(self.bits, 63, 1, set_sd as usize);
    }
}
